﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace Raider.WinForms.Actions.Design
{
    partial class ManagerEditor : Form
    {
        private const string DefaultEvent = "Execute";
        private readonly string defaultEventFormat;

        private readonly List<ControlAction> selectedActions = new List<ControlAction>();
        private readonly ActionManagerDesigner designer;
        private bool isDragging;

        /// <summary>
        /// Required for VS designer support.
        /// </summary>
        protected ManagerEditor()
        {
            InitializeComponent();
        }

        public ManagerEditor(ActionManagerDesigner designer)
        {
            InitializeComponent();
            
            this.designer = designer;
            defaultEventFormat = executeButton.Text;
            versionLabel.Text = "Version: " + ThisAssembly.AssemblyVersion;
        }

        private string SelectedCategory
        {
            get
            {
                return categoriesListBox.SelectedIndex <= 0
                    ? null : categoriesListBox.SelectedItem as string;
            }
        }

        #region Change Notify

        public void CategoryChanged(string category, params ControlAction[] actions)
        {
            if (!string.IsNullOrEmpty(category) && !categoriesListBox.Items.Contains(category))
                FillCategoryList();

            FillActionList();
            SelectActions(true, actions);
        }

        public void NameChanged(string name, ControlAction action)
        {
            if (SelectedCategory == action.Category)
            {
                foreach (ListViewItem item in actionsListView.Items)
                {
                    if (item.Tag == action)
                    {
                        item.Text = name;
                    }
                }
            }
        }

        public void TitleChanged(string title, ControlAction action)
        {
            if (SelectedCategory == action.Category)
            {
                foreach (ListViewItem item in actionsListView.Items)
                {
                    if (item.Tag == action)
                    {
                        item.SubItems[1].Text = title == null 
                            ? null : title.Replace("&", "");
                    }
                }
            }
        }

        public void ImageChanged(Image image, ControlAction action)
        {
            if (SelectedCategory == action.Category)
            {
                foreach (ListViewItem item in actionsListView.Items)
                {
                    if (item.Tag == action)
                    {
                        item.ImageIndex = image == null ? - 1 :
                            imageList.Images.Add(image, Color.Transparent);
                    }
                }
            }
        }

        #endregion

        private void FillCategoryList()
        {
            string selectedCategory = categoriesListBox.SelectedItem as string;

            categoriesListBox.BeginUpdate();

            // Do not remove default category.
            for (int i = categoriesListBox.Items.Count - 1; i > 0; i--)
            {
                categoriesListBox.Items.RemoveAt(i);
            }

            foreach (ControlAction action in designer.ActionManager.Actions)
            {
                // Create category item.
                if (!string.IsNullOrEmpty(action.Category))
                {
                    if (!categoriesListBox.Items.Contains(action.Category))
                    {
                        categoriesListBox.Items.Add(action.Category);
                    }
                }
            }

            categoriesListBox.EndUpdate();

            if (selectedCategory != null)
                categoriesListBox.SelectedItem = selectedCategory;
        }

        private void FillActionList()
        {
            selectedActions.Clear();
            actionsListView.SelectedItems.Clear();
            propertyGrid.SelectedObjects = null;

            if (categoriesListBox.SelectedIndex >= 0)
            {
                // Get actions by category.
                IEnumerable<ControlAction> actions = categoriesListBox.SelectedIndex == 0
                                                         ? GetActionsWithNoCategory(designer.ActionManager.Actions)
                                                         : GetActionsByCategory(designer.ActionManager.Actions, (string)categoriesListBox.SelectedItem);

                // Create items.
                actionsListView.BeginUpdate();
                actionsListView.Items.Clear();
                foreach (var action in actions)
                {
                    // Create list view item.
                    AddListViewItem(action);
                }
                actionsListView.EndUpdate();
            }

            UpdateButtons();
        }

        private void SelectActions(bool focus, params ControlAction[] actions)
        {
            selectedActions.Clear();
            actionsListView.BeginUpdate();
            actionsListView.SelectedItems.Clear();

            if (focus)
            {
                actionsListView.Focus();
            }

            for (int i = 0; i < actions.Length; i++)
            {
                foreach (ListViewItem item in actionsListView.Items)
                {
                    if (item.Tag == actions[i])
                    {
                        item.Selected = true;

                        if (focus && i == actions.Length - 1)
                            actionsListView.FocusedItem = item;
                    }
                }
            }

            actionsListView.EndUpdate();
        }

        private void UpdateButtons()
        {
            deleteCategoryMenuItem.Enabled = categoriesListBox.SelectedIndex > 0;

            deleteMenuItem.Enabled = categoriesListBox.SelectedIndex > 0 ||
                                     actionsListView.SelectedItems.Count > 0 &&
                                     actionsListView.Focused;

            deleteButton.Enabled = deleteCategoryMenuItem.Enabled ||
                                   deleteMenuItem.Enabled;

            executeMenuItem.Enabled = actionsListView.SelectedItems.Count > 0;
            executeButton.Enabled = executeMenuItem.Enabled;

            selectAllToolStripMenuItem.Enabled = actionsListView.SelectedItems.Count < 
                                                 actionsListView.Items.Count;

            // Update Create/Navigate button text.
            if (actionsListView.SelectedItems.Count > 0)
            {
                string defaultEvent = designer.GetDefaultEventName(selectedActions);
                executeButton.Text = string.Format(defaultEventFormat, defaultEvent);
                executeMenuItem.Text = string.Format(defaultEventFormat, defaultEvent);
            }
        }

        private void OnFormLoad(object sender, EventArgs e)
        {
            Font = SystemFonts.MessageBoxFont;

            toolStrip.Renderer = designer.ToolStripRenderer;
            propertyGrid.ToolStripRenderer = designer.ToolStripRenderer;
            actionsMenuStrip.Renderer = designer.ToolStripRenderer;
            categoriesMenuStrip.Renderer = designer.ToolStripRenderer;
            mainSplitContainer.BackColor = propertyGrid.BackColor;

            executeButton.Text = string.Format(defaultEventFormat, DefaultEvent);
            executeMenuItem.Text = string.Format(defaultEventFormat, DefaultEvent);

            Text += designer.ActionManager.Site.Name;

            // Add Events property tab.
            propertyGrid.AddEventsTab(designer.Component.Site);

            // Fill categories and actions.
            FillCategoryList();
        }

        private void OnFormShown(object sender, EventArgs e)
        {
            // Adjust size of the last column.
            actionsListView.Columns[actionsListView.Columns.Count - 1].Width = -2;

            if (categoriesListBox.SelectedIndex < 0)
                categoriesListBox.SelectedIndex = 0;
        }

        #region Helper Functions

        private ListViewItem AddListViewItem(ControlAction action)
        {
            var title = action.Title != null ? action.Title.Replace("&", "") : null;
            var item = new ListViewItem(new[] { action.Site.Name, title });
            item.ImageIndex = action.Image == null ? -1 : imageList.Images.Add(action.Image, Color.Transparent);
            item.Tag = action;
            actionsListView.Items.Add(item);
            return item;
        }

        private static List<ControlAction> GetActionsByCategory(IEnumerable<IAction> actions, string category)
        {
            var list = new List<ControlAction>();
            foreach (ControlAction item in actions)
            {
                if (item.Category == category)
                    list.Add(item);
            }

            return list;
        }

        private static List<ControlAction> GetActionsWithNoCategory(IEnumerable<IAction> actions)
        {
            var list = new List<ControlAction>();
            foreach (ControlAction item in actions)
            {
                if (string.IsNullOrEmpty(item.Category))
                    list.Add(item);
            }

            return list;
        }

        #endregion

        private void OnActionsMouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (executeButton.Enabled)
                executeButton.PerformClick();
        }

        private void OnCategoriesSelectedIndexChanged(object sender, EventArgs e)
        {
            if (!isDragging)
            {
                UpdateButtons();
                FillActionList();
            }
        }

        private int selectedTab;

        private void OnActionsItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
        {
            if (e.IsSelected)
            {
                selectedActions.Add((ControlAction)e.Item.Tag);
            }
            else
            {
                selectedActions.Remove((ControlAction)e.Item.Tag);
            }

            if (propertyGrid.SelectedObjects != null &&
                propertyGrid.SelectedObjects.Length > 0)
            {
                selectedTab = propertyGrid.SelectedTabIndex;
            }

            if (selectedActions.Count > 0)
            {
                propertyGrid.SuspendLayout();
                propertyGrid.SelectedObjects = selectedActions.ToArray();
                propertyGrid.SelectTab(selectedTab);
                propertyGrid.PerformLayout();
            }
            else
            {
                propertyGrid.SelectedObjects = null;
            }
        }

        #region Drag & Drop

        int mouseDownX, mouseDownY;

        private void OnActionsMouseDown(object sender, MouseEventArgs e)
        {
            mouseDownX = e.X;
            mouseDownY = e.Y;
        }

        private void OnActionsMouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button != MouseButtons.Left || e.X == mouseDownX && e.Y == mouseDownY)
                return;

            ListViewHitTestInfo hitTest = actionsListView.HitTest(e.Location);
            if (hitTest.Item != null)
            {
                // Get actions to move.
                var actionsToMove = selectedActions.ToArray();

                try
                {
                    isDragging = true;
                    DoDragDrop(actionsToMove, DragDropEffects.Move);
                }
                finally
                {
                    isDragging = false;
                }
            }
        }

        private void OnCategoriesDragOver(object sender, DragEventArgs e)
        {
            int index = CategoryIndexFromPoint(e.X, e.Y);
            if (index >= 0)
            {
                categoriesListBox.SelectedIndex = index;
                e.Effect = DragDropEffects.Move;
            }
        }

        private void OnCategoriesDragDrop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(ControlAction[])))
            {
                int index = CategoryIndexFromPoint(e.X, e.Y);
                string category = index == 0 ? null : (string)categoriesListBox.Items[index];

                // Change actions' categories.
                var actions = (ControlAction[])e.Data.GetData(typeof(ControlAction[]));
                designer.ChangeCategory(category, actions);
            }
        }

        private int CategoryIndexFromPoint(int x, int y)
        {
            Point local = categoriesListBox.PointToClient(new Point(x, y));
            return categoriesListBox.IndexFromPoint(local);
        }

        #endregion

        private void OnNewAction(object sender, EventArgs e)
        {
            if (categoriesListBox.SelectedIndex < 0)
                categoriesListBox.SelectedIndex = 0;

            // Add a new action to the collection.
            ControlAction action = designer.AddAction<ControlAction>(SelectedCategory);

            // Create list view item.
            AddListViewItem(action);

            // Select list view item.
            SelectActions(true, action);
        }

        private void OnDelete(object sender, EventArgs e)
        {
            if (actionsListView.Focused && selectedActions.Count > 0)
            {
                // Delete actions.
                var result = MessageBox.Show(
                    "Do you really want to delete selected action(s)?",
                    Application.ProductName, MessageBoxButtons.OKCancel);

                if (result == DialogResult.OK)
                {
                    designer.DeleteActions(selectedActions.ToArray());

                    // Remove list view items.
                    actionsListView.BeginUpdate();
                    foreach (ListViewItem item in actionsListView.SelectedItems)
                    {
                        actionsListView.Items.Remove(actionsListView.SelectedItems[0]);
                    }
                    actionsListView.EndUpdate();
                }
            }
            else if (categoriesListBox.SelectedIndex > 0)
            {
                // Delete category.
                var result = MessageBox.Show(
                    string.Format("Do you really want to delete {0} category?", SelectedCategory),
                    Application.ProductName, MessageBoxButtons.OKCancel);

                if (result == DialogResult.OK)
                {
                    var actions = GetActionsByCategory(designer.ActionManager.Actions, SelectedCategory);
                    designer.DeleteActions(actions.ToArray());

                    // Remove category item.
                    var index = categoriesListBox.SelectedIndex;
                    categoriesListBox.Items.RemoveAt(index);

                    // Select previos category.
                    if (index < categoriesListBox.Items.Count ||
                        --index < categoriesListBox.Items.Count)
                        categoriesListBox.SelectedIndex = index;
                }
            }
        }

        private void OnExecuteButtonClick(object sender, EventArgs e)
        {
            if (selectedActions.Count > 0)
            {
                designer.GenerateEventHandler(selectedActions.ToArray());
                Close();
            }
        }

        private void OnActionsSelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateButtons();
        }

        private void OnNewCategoryClick(object sender, EventArgs e)
        {
            // Generate name for new category.
            int i = 0;
            string category;
            do
            {
                i++;
                category = "Category" + i;
            }
            while (categoriesListBox.Items.Contains(category));

            // Create and select new category.
            categoriesListBox.SelectedIndex = categoriesListBox.Items.Add(category);
        }

        private void OnStandardActionClick(object sender, EventArgs e)
        {
            using (StandardActionDialog form = new StandardActionDialog())
            {
                if (form.ShowDialog() == DialogResult.OK)
                {
                    // Add a new actions to the collection.
                    var types = form.GetSelectedActionTypes();
                    ControlAction[] actions = designer.AddActions(null, types);

                    // Return if no actions was selected.
                    if (actions == null || actions.Length == 0)
                        return;

                    // Find category (we assume that all actions 
                    // returned by GetSelectedActionTypes() has same
                    // category.
                    bool create = true;
                    if (string.IsNullOrEmpty(actions[0].Category))
                    {
                        if (categoriesListBox.SelectedIndex != 0)
                        {
                            categoriesListBox.SelectedIndex = 0;
                            create = false;
                        }
                    }
                    else if (!string.Equals(categoriesListBox.SelectedItem, actions[0].Category))
                    {
                        if (categoriesListBox.Items.Contains(actions[0].Category))
                        {
                            categoriesListBox.Items.IndexOf(actions[0].Category);
                        }
                        else
                        {
                            categoriesListBox.Items.Add(actions[0].Category);
                        }

                        categoriesListBox.SelectedItem = actions[0].Category;
                        create = false;
                    }

                    // Create list view item.
                    if (create)
                    {
                        actionsListView.BeginUpdate();
                        foreach (var action in actions)
                        {
                            AddListViewItem(action);
                        }
                        actionsListView.EndUpdate();
                    }

                    // Select list view item.
                    SelectActions(true, actions);
                }
            }
        }

        private void OnSelectAllClick(object sender, EventArgs e)
        {
            foreach (ListViewItem item in actionsListView.Items)
            {
                item.Selected = true;
            }
        }

        private void OnPropertyGridPropertyItemDoubleClicked(object sender, EventArgs e)
        {
            if (propertyGrid.SelectedTabIndex > 0)
            {
                Close();
            }
        }
    }
}
